//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package vincent.com.layouttest.multidex;

import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.os.Build.VERSION;
import android.util.Log;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.ZipFile;

import dalvik.system.DexFile;
import vincent.com.layouttest.util.LogUtil;

public final class MultiDex {
    static final String TAG = "MultiDex";
    private static final String OLD_SECONDARY_FOLDER_NAME = "secondary-dexes";
    private static final String SECONDARY_FOLDER_NAME;
    private static final int MAX_SUPPORTED_SDK_VERSION = 20;
    private static final int MIN_SDK_VERSION = 4;
    private static final int VM_WITH_MULTIDEX_VERSION_MAJOR = 2;
    private static final int VM_WITH_MULTIDEX_VERSION_MINOR = 1;
    private static final Set<String> installedApk;
    private static final boolean IS_VM_MULTIDEX_CAPABLE;

    private MultiDex() {
    }


    public enum MultiDexType {
        MUST("must"), THIRDPARTY("3rdparty"), KUGOU2RD("kugou2rd"), MODULEDLNA("moduledlna"),
        ANDROIDFANXING("androidfanxing"), ANDROIDGAME("androidgame"), ANDROIDKTV("androidktv"),
        MODULEFM("modulefm"), MODULERINGTONE("moduleringtone"), MODULETRANSFER("moduletransfer"), MODULENETWORKTEST("modulenetworktest"),
        MODULEHELLO("Hello");

        private String name;

        private MultiDexType(String name) {
            this.name = name;
        }

        public String getName() {
            return name;
        }

        @Override
        public String toString() {
            return name;
        }

    }

    public static DexLoadResult install(Context context, MultiDexType type) {
        DexLoadResult mLoadResult = new DexLoadResult();
        Log.d("MultiDex", "install");
//        if (IS_VM_MULTIDEX_CAPABLE) {
//            Log.d("MultiDex", "VM has multidex support, MultiDex support library is disabled.");
//        } else
        if (VERSION.SDK_INT < 4) {
            throw new RuntimeException("Multi dex installation failed. SDK " + VERSION.SDK_INT + " is unsupported. Min SDK version is " + 4 + ".");
        } else {
            try {
                ApplicationInfo e = getApplicationInfo(context);
                if (e == null) {
                    mLoadResult.errorInfo = "ApplicationInfo is null";
                    return mLoadResult;
                }
                Set var2 = installedApk;
                synchronized (installedApk) {
                    String apkPath = e.sourceDir;
//                    if (installedApk.contains(apkPath)) {
//                        return mLoadResult;
//                    }

//                    installedApk.add(apkPath);
                    if (VERSION.SDK_INT > 20) {
                        Log.w("MultiDex", "MultiDex is not guaranteed to work in SDK version " + VERSION.SDK_INT + ": SDK version higher than " + 20 + " should be backed by " + "runtime with built-in multidex capabilty but it\'s not the " + "case here: java.vm.version=\"" + System.getProperty("java.vm.version") + "\"");
                    }

                    ClassLoader loader;
                    try {
                        loader = context.getClassLoader();
                    } catch (RuntimeException var9) {
                        mLoadResult.errorInfo = "Failure while trying to obtain Context class loader";
                        Log.w("MultiDex", "Failure while trying to obtain Context class loader. Must be running in test mode. Skip patching.", var9);
                        return mLoadResult;
                    }

                    if (loader == null) {
                        mLoadResult.errorInfo = "Context class loader is null";
                        Log.e("MultiDex", "Context class loader is null. Must be running in test mode. Skip patching.");
                        return mLoadResult;
                    }

                    try {
                        clearOldDexDir(context);
                    } catch (Throwable var8) {
                        Log.w("MultiDex", "Something went wrong when trying to clear old MultiDex extraction, continuing without cleaning.", var8);
                    }

                    File dexDir = new File(e.dataDir, SECONDARY_FOLDER_NAME);
                    List files = MultiDexExtractor.load(context, e, dexDir, false, type);
                    if (files.isEmpty()) {
                        return mLoadResult;
                    }
                    if (checkValidZipFiles(files)) {
                        installSecondaryDexes(loader, dexDir, files);
                    } else {
                        Log.w("MultiDex", "Files were not valid zip files.  Forcing a reload.");
                        files = MultiDexExtractor.load(context, e, dexDir, true, type);
                        if (!checkValidZipFiles(files)) {
                            throw new RuntimeException("Zip files were not valid.");
                        }

                        installSecondaryDexes(loader, dexDir, files);
                    }
                }
            } catch (Exception var11) {
                Log.e("MultiDex", "Multidex installation failure", var11);
                throw new RuntimeException("Multi dex installation failed (" + var11.getMessage() + ").");
            }
            mLoadResult.result = true;
            Log.d("MultiDex", "install done");
        }

        return mLoadResult;
    }

    private static ApplicationInfo getApplicationInfo(Context context) throws NameNotFoundException {
        PackageManager pm;
        String packageName;
        try {
            pm = context.getPackageManager();
            packageName = context.getPackageName();
        } catch (RuntimeException var4) {
            Log.w("MultiDex", "Failure while trying to obtain ApplicationInfo from Context. Must be running in test mode. Skip patching.", var4);
            return null;
        }

        if (pm != null && packageName != null) {
            ApplicationInfo applicationInfo = pm.getApplicationInfo(packageName, 128);
            return applicationInfo;
        } else {
            return null;
        }
    }

    static boolean isVMMultidexCapable(String versionString) {
        boolean isMultidexCapable = false;
        if (versionString != null) {
            Matcher matcher = Pattern.compile("(\\d+)\\.(\\d+)(\\.\\d+)?").matcher(versionString);
            if (matcher.matches()) {
                try {
                    int e = Integer.parseInt(matcher.group(1));
                    int minor = Integer.parseInt(matcher.group(2));
                    isMultidexCapable = e > 2 || e == 2 && minor >= 1;
                } catch (NumberFormatException var5) {
                    ;
                }
            }
        }

        Log.d("MultiDex", "VM with version " + versionString + (isMultidexCapable ? " has multidex support" : " does not have multidex support"));
        return isMultidexCapable;
    }

    private static void installSecondaryDexes(ClassLoader loader, File dexDir, List<File> files) throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, InvocationTargetException, NoSuchMethodException, IOException,Exception {

        if (!files.isEmpty()) {
            try {
                if (VERSION.SDK_INT >= 23) {
                    MultiDex.V23.install(loader, files, dexDir);
                } else if (VERSION.SDK_INT >= 19) {
                    MultiDex.V19.install(loader, files, dexDir);
                } else if (VERSION.SDK_INT >= 14) {
                    MultiDex.V14.install(loader, files, dexDir);
                } else {
                    MultiDex.V4.install(loader, files);
                }
            } catch (Exception e) {
                // 此处抛出异常，可能由于低版本SDK使用了高版本SDK的PathClassLoader,反之亦有可能，尝试重新加载
                if (VERSION.SDK_INT >= 23) {
                    throw new Exception(e);
                } else if (VERSION.SDK_INT >= 19) {
                    MultiDex.V23.install(loader, files, dexDir);
                } else if (VERSION.SDK_INT >= 14) {
                    MultiDex.V4.install(loader, files);
                } else {
                    MultiDex.V14.install(loader, files, dexDir);
                }
            }
        }

    }

    private static boolean checkValidZipFiles(List<File> files) {
        Iterator i$ = files.iterator();

        File file;
        do {
            if (!i$.hasNext()) {
                return true;
            }

            file = (File) i$.next();
        } while (MultiDexExtractor.verifyZipFile(file));

        return false;
    }

    private static Field findField(Object instance, String name) throws NoSuchFieldException {
        Class clazz = instance.getClass();

        while (clazz != null) {
            try {
                Field e = clazz.getDeclaredField(name);
                if (!e.isAccessible()) {
                    e.setAccessible(true);
                }

                return e;
            } catch (NoSuchFieldException var4) {
                clazz = clazz.getSuperclass();
            }
        }

        throw new NoSuchFieldException("Field " + name + " not found in " + instance.getClass());
    }

    private static Method findMethod(Object instance, String name, Class... parameterTypes) throws NoSuchMethodException {
        Class clazz = instance.getClass();

        while (clazz != null) {
            try {
                Method e = clazz.getDeclaredMethod(name, parameterTypes);
                if (!e.isAccessible()) {
                    e.setAccessible(true);
                }

                return e;
            } catch (NoSuchMethodException var5) {
                clazz = clazz.getSuperclass();
            }
        }

        throw new NoSuchMethodException("Method " + name + " with parameters " + Arrays.asList(parameterTypes) + " not found in " + instance.getClass());
    }

    private static void expandFieldArray(Object instance, String fieldName, Object[] extraElements) throws NoSuchFieldException, IllegalArgumentException, IllegalAccessException {
        Field jlrField = findField(instance, fieldName);
        Object[] original = (Object[]) ((Object[]) jlrField.get(instance));
        Object[] combined = (Object[]) ((Object[]) Array.newInstance(original.getClass().getComponentType(), original.length + extraElements.length));
        System.arraycopy(original, 0, combined, 0, original.length);
        System.arraycopy(extraElements, 0, combined, original.length, extraElements.length);
        jlrField.set(instance, combined);
    }

    static {
        SECONDARY_FOLDER_NAME = "app_dex";
        installedApk = new HashSet();
        IS_VM_MULTIDEX_CAPABLE = isVMMultidexCapable(System.getProperty("java.vm.version"));
    }

    private static void clearOldDexDir(Context context) throws Exception {
        File dexDir = new File(context.getFilesDir().getAbsolutePath());
        if (dexDir.isDirectory()) {
            Log.d("MultiDex", "Clearing old secondary dex dir (" + dexDir.getPath() + ").");
            File[] files = dexDir.listFiles();
            if (files == null) {
                Log.w("MultiDex", "Failed to list secondary dex dir content (" + dexDir.getPath() + ").");
                return;
            }

            File[] arr$ = files;
            int len$ = files.length;

            for (int i$ = 0; i$ < len$; ++i$) {
                File oldFile = arr$[i$];
                if (oldFile.getName().endsWith(".jar")) {
                    Log.d("MultiDex", "Trying to delete old file " + oldFile.getPath() + " of size " + oldFile.length());
                    if (!oldFile.delete()) {
                        Log.w("MultiDex", "Failed to delete old file " + oldFile.getPath());
                    } else {
                        Log.d("MultiDex", "Deleted old file " + oldFile.getPath());
                    }
                }
            }

//            if(!dexDir.delete()) {
//                Log.w("MultiDex", "Failed to delete secondary dex dir " + dexDir.getPath());
//            } else {
//                Log.d("MultiDex", "Deleted old secondary dex dir " + dexDir.getPath());
//            }
        }

    }

    private static final class V4 {
        private V4() {
        }

        private static void install(ClassLoader loader, List<File> additionalClassPathEntries) throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, IOException, Exception {
            int extraSize = additionalClassPathEntries.size();
            Field pathField = MultiDex.findField(loader, "path");
            StringBuilder path = new StringBuilder((String) pathField.get(loader));
            String[] extraPaths = new String[extraSize];
            File[] extraFiles = new File[extraSize];
            ZipFile[] extraZips = new ZipFile[extraSize];
            DexFile[] extraDexs = new DexFile[extraSize];

            String entryPath;
            int index;
            for (ListIterator iterator = additionalClassPathEntries.listIterator(); iterator.hasNext(); extraDexs[index] = DexFile.loadDex(entryPath, entryPath + ".dex", 0)) {
                File additionalEntry = (File) iterator.next();
                entryPath = additionalEntry.getAbsolutePath();
                path.append(':').append(entryPath);
                index = iterator.previousIndex();
                extraPaths[index] = entryPath;
                extraFiles[index] = additionalEntry;
                extraZips[index] = new ZipFile(additionalEntry);
            }

            pathField.set(loader, path.toString());
            MultiDex.expandFieldArray(loader, "mPaths", extraPaths);
            MultiDex.expandFieldArray(loader, "mFiles", extraFiles);
            MultiDex.expandFieldArray(loader, "mZips", extraZips);
            try {
                MultiDex.expandFieldArray(loader, "mDexs", extraDexs);
            } catch (Exception e) {
                e.printStackTrace();

                //失败后尝试适配阿里云
                Object[] mLexs = new Object[extraDexs.length];
                for (int i = 0; i < extraDexs.length; i++) {
                    // 将DexFile值赋值给LexFile
                    Object lexFile = null;
                    try {
                        Class class1 = Class.forName("dalvik.system.LexFile");
                        Class[] argtype = new Class[]{
                                String.class, int.class
                        };
//                  public Lex(File file)
//                  public Lex(String mFileName)
//                  public Lex(String mFileName,int type)
//                  TYPE_DIR 2 |TYPE_LEX 1 | TYPE_UNKNOWN 0 | TYPE_ZIP 3
                        Object mFileName = getFieldValue(extraDexs[i], "mFileName");
                        // LexFile没有无参构造，这里获取了一个传String的构造，这里必须直接给定mFileName的值，LexFile代码里做了判断，路径不对直接捕获异常，没有抛出
                        // type默认是3，部分阿里云必须在构造函数里直接指明TYPE,这里传1，否则初始化失败
                        Object[] argparam = new Object[]{
                                mFileName, 1
                        };
                        Constructor constructor = class1.getDeclaredConstructor(argtype);
                        constructor.setAccessible(true);
                        lexFile = constructor.newInstance(argparam);
                        LogUtil.e("DexClassLoader", "获取lexFile 实例成功");

                        Object mCookie = getFieldValue(extraDexs[i], "mCookie");
                        setFieldValue(lexFile, "mCookie", mCookie);
                    } catch (Exception e1) {
                        e1.printStackTrace();
                    }
                    if(lexFile != null){
                        mLexs[i] = lexFile;
                    }
                }

                MultiDex.expandFieldArray(loader, "mLexs", mLexs);

            }

        }
    }

    private static final class V14 {
        private V14() {
        }

        private static void install(ClassLoader loader, List<File> additionalClassPathEntries, File optimizedDirectory) throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, InvocationTargetException, NoSuchMethodException {
            Field pathListField = MultiDex.findField(loader, "pathList");
            Object dexPathList = pathListField.get(loader);
            MultiDex.expandFieldArray(dexPathList, "dexElements", makeDexElements(dexPathList, new ArrayList(additionalClassPathEntries), optimizedDirectory));
        }

        private static Object[] makeDexElements(Object dexPathList, ArrayList<File> files, File optimizedDirectory) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
            Method makeDexElements = MultiDex.findMethod(dexPathList, "makeDexElements", new Class[]{ArrayList.class, File.class});
            return (Object[]) ((Object[]) makeDexElements.invoke(dexPathList, new Object[]{files, optimizedDirectory}));
        }
    }

    private static final class V19 {
        private V19() {
        }

        private static void install(ClassLoader loader, List<File> additionalClassPathEntries, File optimizedDirectory) throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, InvocationTargetException, NoSuchMethodException {
            Field pathListField = MultiDex.findField(loader, "pathList");
            Object dexPathList = pathListField.get(loader);
            ArrayList suppressedExceptions = new ArrayList();
            MultiDex.expandFieldArray(dexPathList, "dexElements", makeDexElements(dexPathList, new ArrayList(additionalClassPathEntries), optimizedDirectory, suppressedExceptions));
            if (suppressedExceptions.size() > 0) {
                Iterator suppressedExceptionsField = suppressedExceptions.iterator();

                while (suppressedExceptionsField.hasNext()) {
                    IOException dexElementsSuppressedExceptions = (IOException) suppressedExceptionsField.next();
                    Log.w("MultiDex", "Exception in makeDexElement", dexElementsSuppressedExceptions);
                }

                Field suppressedExceptionsField1 = MultiDex.findField(loader, "dexElementsSuppressedExceptions");
                IOException[] dexElementsSuppressedExceptions1 = (IOException[]) ((IOException[]) suppressedExceptionsField1.get(loader));
                if (dexElementsSuppressedExceptions1 == null) {
                    dexElementsSuppressedExceptions1 = (IOException[]) suppressedExceptions.toArray(new IOException[suppressedExceptions.size()]);
                } else {
                    IOException[] combined = new IOException[suppressedExceptions.size() + dexElementsSuppressedExceptions1.length];
                    suppressedExceptions.toArray(combined);
                    System.arraycopy(dexElementsSuppressedExceptions1, 0, combined, suppressedExceptions.size(), dexElementsSuppressedExceptions1.length);
                    dexElementsSuppressedExceptions1 = combined;
                }

                suppressedExceptionsField1.set(loader, dexElementsSuppressedExceptions1);
            }

        }

        private static Object[] makeDexElements(Object dexPathList, ArrayList<File> files, File optimizedDirectory, ArrayList<IOException> suppressedExceptions) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
            Method makeDexElements = MultiDex.findMethod(dexPathList, "makeDexElements", new Class[]{ArrayList.class, File.class, ArrayList.class});
            return (Object[]) ((Object[]) makeDexElements.invoke(dexPathList, new Object[]{files, optimizedDirectory, suppressedExceptions}));
        }
    }

    private static final class V23 {
        private V23(){
        }

        private static void install(ClassLoader loader, List<File> additionalClassPathEntries, File optimizedDirectory) throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, InvocationTargetException, NoSuchMethodException {
            Field pathListField = MultiDex.findField(loader, "pathList");
            Object dexPathList = pathListField.get(loader);
            ArrayList suppressedExceptions = new ArrayList();
            MultiDex.expandFieldArray(dexPathList, "dexElements", makePathElements(dexPathList, new ArrayList(additionalClassPathEntries), optimizedDirectory, suppressedExceptions));

            if (suppressedExceptions.size() > 0) {
                Iterator suppressedExceptionsField = suppressedExceptions.iterator();

                while (suppressedExceptionsField.hasNext()) {
                    IOException dexElementsSuppressedExceptions = (IOException) suppressedExceptionsField.next();
                    Log.w("MultiDex", "Exception in makeDexElement", dexElementsSuppressedExceptions);
                }

                Field suppressedExceptionsField1 = MultiDex.findField(loader, "dexElementsSuppressedExceptions");
                IOException[] dexElementsSuppressedExceptions1 = (IOException[]) ((IOException[]) suppressedExceptionsField1.get(loader));
                if (dexElementsSuppressedExceptions1 == null) {
                    dexElementsSuppressedExceptions1 = (IOException[]) suppressedExceptions.toArray(new IOException[suppressedExceptions.size()]);
                } else {
                    IOException[] combined = new IOException[suppressedExceptions.size() + dexElementsSuppressedExceptions1.length];
                    suppressedExceptions.toArray(combined);
                    System.arraycopy(dexElementsSuppressedExceptions1, 0, combined, suppressedExceptions.size(), dexElementsSuppressedExceptions1.length);
                    dexElementsSuppressedExceptions1 = combined;
                }

                suppressedExceptionsField1.set(loader, dexElementsSuppressedExceptions1);
            }

        }

        private static Object[] makePathElements(Object dexPathList, ArrayList<File> files, File optimizedDirectory, ArrayList<IOException> suppressedExceptions) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
            Method makeDexElements = MultiDex.findMethod(dexPathList, "makePathElements", new Class[]{List.class, File.class, List.class});
            return (Object[]) ((Object[]) makeDexElements.invoke(dexPathList, new Object[]{files, optimizedDirectory, suppressedExceptions}));
        }

    }


    /**
     * 反射获得对象属性，由于dex加载时要用到异常编程，很多地方需要反射失败时捕获更换方案，所以反射失败时需要将异常抛出，不要直接调用ReflectionUtils.getFieldValue
     *
     * @param object
     * @param fieldName
     * @return
     * @throws Exception
     */
    protected static Object getFieldValue(Object object, String fieldName) throws Exception {
        // 根据 对象和属性名通过反射 调用上面的方法获取 Field对象
        Field field = ReflectionUtils.getDeclaredField(object, fieldName);
        // 抑制Java对其的检查
        field.setAccessible(true);
        try {
            // 获取 object 中 field 所代表的属性值
            return field.get(object);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    protected static void setFieldValue(Object object, String fieldName, Object value) throws Exception {
        // 根据 对象和属性名通过反射 调用上面的方法获取 Field对象
        Field field = ReflectionUtils.getDeclaredField(object, fieldName);
        // 抑制Java对其的检查
        field.setAccessible(true);
        try {
            // 将 object 中 field 所代表的值 设置为 value
            field.set(object, value);
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
